#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>

#define RANDOM01 (REAL)drand48()

#define DEVICONST __device__ __constant__
#define DEVIBASE __device__


#define V(side) ((side)*(side)*(side))
#define NUMBER_OF_PLANES 3
#define NUMBER_OF_VARIABLES 6 //three spin components and three coupling variables
#define TOTALHALO 2
#define SHM_FOR_ARGUMENTS 256
#define HALFWARP 16
#define BLOCKSIZE_X (2*HALFWARP)

//-------------------------------------------------------------------------------------------------------
// Variable Host Declaration
//-------------------------------------------------------------------------------------------------------
int init_type_j;

int h_nspin;
int h_side;
int nsteps;
int overrelaxiter;
int outfreq;
int dumpfreq;
int seed;
int shmpervar, shmnitemspp;
int blocksize_x=BLOCKSIZE_X;

int ns_j;

size_t ms_s;
size_t ms_j;

int numBlocks;
int numThreadsPerBlock;

REAL* h_a;
REAL* h_b;
REAL* h_c;

REAL* h_Jpx;
REAL* h_Jpy;
REAL* h_Jpz;

int h_sdbL;
int h_sdbLs;
int h_maskL;
int h_maskL2;
int h_maskL3;
int h_Ldb2;
int h_Lsdb2;

REAL *h_Energy;
REAL  h_totene;

REAL h_h;
REAL h_Beta;
REAL zero, one, two, three, zerofive, zerotwentyfive;
REALENE zeroEne; //MESSA SOLO PER IL CALCOLO DELL'ENERGIA IN DOUBLE COME PROVA

bool onlyOverrelaxation = false;
bool onlyHeatBath = false;

int randNum; // DA TOGLIERE MESSO PER TEST RIMETTERE LOCALE

int verbose=0;


//-------------------------------------------------------------------------------------------------------
// Variable Device Declaration
//-------------------------------------------------------------------------------------------------------
DEVIBASE REAL* d_Ra;
DEVIBASE REAL* d_Rb;
DEVIBASE REAL* d_Rc;
DEVIBASE REAL* d_Ba;
DEVIBASE REAL* d_Bb;
DEVIBASE REAL* d_Bc;

DEVIBASE REAL* d_Jpx;
DEVIBASE REAL* d_Jpy;
DEVIBASE REAL* d_Jpz;
DEVIBASE REAL* d_Jmx;
DEVIBASE REAL* d_Jmy;
DEVIBASE REAL* d_Jmz;

DEVIBASE REAL* d_Energy;
static  REAL* r_odata;

DEVICONST int d_side;
DEVICONST int d_nspin;

DEVICONST int d_sdbL;
DEVICONST int d_sdbLs;
DEVICONST int d_maskL;
DEVICONST int d_maskL2;
DEVICONST int d_maskL3;
DEVICONST int d_Ldb2;
DEVICONST int d_Lsdb2;

DEVICONST int jindred[2];
DEVICONST int jindblue[2];

DEVICONST REAL d_h;
DEVICONST REAL d_Beta;
DEVICONST REAL d_const_one, d_const_two, d_const_zerofive, d_const_zerotwentyfive;
DEVICONST int d_const_shmpervar, d_const_shmnitemspp;


//-------------------------------------------------------------------------------------------------------
// Function Declaration
//-------------------------------------------------------------------------------------------------------
__global__ void Overrelaxation(REAL *a, REAL *b, REAL *c,
                               REAL *Jpx, REAL *Jpy, REAL *Jpz, int odditer);

__global__ void ComputeEnergy(REAL *a, REAL *b, REAL *c, REAL *Jpx, REAL *Jpy, REAL *Jpz, REAL *Energy);

__global__ void HeatBath(REAL *a, REAL *b, REAL *c,
			 REAL *Jpx, REAL *Jpy, REAL *Jpz,
			 REAL *atry, REAL *btry, REAL *ctry,
			 REAL *random, int odditer);

extern "C" void reduceSinglePass(int, int , int, REAL *, REAL *);

void ComputeHEnergy(REAL *a, REAL *b, REAL *c, REAL *Jpx, REAL *Jpy, REAL *Jpz,REAL *energy);

void Init(void);
void CopyConstOnDevice(void);
int findlog2(int n);
void FreeDevice(void);
void FreeHost(void);
void Initialize_Spin(REAL* sa_a, REAL* sa_b, REAL* sa_c);
void CopyDataOnDevice(void);
void Initialize_J(void);
REAL rand_gauss(void);
void Parameters_Reading(char *inputfile);
void Calculate_Sizes(void);
void checkCUDAError(const char *msg);
void Dump_Spin(int count);
